﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Enough.Lifecycle.ViewModel
{
    /// <summary>
    /// Helps you to locate view model implementations. Use Register&lt;TypeOfViewModel&gt;() to register view models.
    /// In your App.xaml, add the locator like this:
    /// &lt;Application.Resources&gt;
    ///    &lt;ResourceDictionary&gt;
    ///        &lt;viewmodel:ViewModelLocator x:Key="Locator" d:IsDataSource="True"/&gt;
    ///
    /// </summary>
    public class BaseViewModelLocator : IDictionary<string, object>
    {
        private Dictionary<string, Type> _typesByKey;
        private Dictionary<string, object> _implementationsByKey;
        private object _lockObject = new object();

        protected BaseViewModelLocator()
        {
            _typesByKey = new Dictionary<string, Type>();
            _implementationsByKey = new Dictionary<string, object>();
        }

        /// <summary>
        /// Registers a new view model implementation
        /// </summary>
        /// <typeparam name="T">the type</typeparam>
        /// <param name="key">the name, when not defined the implementation type's name is used. When this ends with "ViewModel", only the beginning is used.</param>
        public void Register<T>(string key = null) where T : class
        {
            Type t = typeof(T);
            if (key == null)
            {
                key = t.Name;
                int index = key.IndexOf("ViewModel");
                if (index > 0)
                {
                    key = key.Substring(0, index);
                }
            }
            _typesByKey[key] = typeof(T);
        }

        public void Add(string key, object value)
        {
            _implementationsByKey.Add(key, value);
        }

        public bool ContainsKey(string key)
        {
            return _typesByKey.ContainsKey(key);
        }

        public ICollection<string> Keys
        {
            get { return _typesByKey.Keys; }
        }

        public bool Remove(string key)
        {
            lock (_lockObject)
            {
                bool success = _typesByKey.Remove(key);
                _implementationsByKey.Remove(key);
                return success;
            }
        }

        public bool TryGetValue(string key, out object value)
        {
            lock (_lockObject)
            {
                if (!_implementationsByKey.ContainsKey(key))
                {
                    if (!_typesByKey.ContainsKey(key))
                    {
                        value = null;
                        return false;
                    }
                    Type t = _typesByKey[key];
                    value = Activator.CreateInstance(t);
                    _implementationsByKey.Add(key, value);
                    return true;
                }
                value = _implementationsByKey[key];
                return true;
            }
        }

        public ICollection<object> Values
        {
            get { return _implementationsByKey.Values; }
        }

        public object this[string key]
        {
            get
            {
                object value;
                TryGetValue(key, out value);
                return value;
            }
            set
            {
                _implementationsByKey[key] = value;
            }
        }

        public void Add(KeyValuePair<string, object> item)
        {
            _implementationsByKey.Add(item.Key, item.Value);
        }

        public void Clear()
        {
            _typesByKey.Clear();
            _implementationsByKey.Clear();
        }

        public bool Contains(KeyValuePair<string, object> item)
        {
            return _typesByKey.ContainsKey(item.Key) && this[item.Key] == item.Value;
        }

        public void CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
        {
            throw new NotImplementedException();
        }

        public int Count
        {
            get { return _typesByKey.Count; }
        }

        public bool IsReadOnly
        {
            get { return true; }
        }

        public bool Remove(KeyValuePair<string, object> item)
        {
            if (!Contains(item))
            {
                return false;
            }
            return Remove(item.Key);
        }

        public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
        {
            return _implementationsByKey.GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return _implementationsByKey.GetEnumerator();
        }
    }

}
